Configurar y utilizar las interrupciones de hardware para detectar entradas y realizar acciones en consecuencia.
En el Atmega168pa tenemos dos puertos especificos para los hardware interrupts:
Y a mayores todos los registros de puertos (B, C y D) tienen sus propios interrupts, PinChange Interrupt, pero en este caso es un interrupt por cada registro, por lo que una vez disparado el interrupt tendremos que comprobar el puerto exacto que ha generado esa interrupción
Para utilizar los interrupts con los puertos especificos (INT0 e INT1) tenemos que realizar los siguientes pasos:
#include <avr/interrupt.h>
EIMSK |= (1 << INT0);
EICRA |= (1 << ISC00);
En el datasheet tenemos todas las configuraciones disponibles para ambos puertos según el comportamiento que queramos:
Tenemos que tener en cuenta que si tenemos configurado los puertos con pull-up resistor las entradas estarán invertidas por lo tanto cuando en la tabla se indica "rising" quiere decir soltando en vez de pulsando y viceversa con "falling"
ISR(INT0_vect) {}
Podemos definir otra función ISR a mayores para usar con INT1 de manera simultanea usando INT1_vect como argumento (a mayores de realizar los pasos previos para activar INT1), en verdad ISR() no es una función sino un macro...
sei();
Para activar las interrupciones en los registros de los puertos estandar (B, C y D) lo hacemos de la siguiente manera:
NOTA: en estos puertos no se puede indicar cual es el evento que genera la interrupción (ya sea al pulsar, soltar...etc) como con INT0 e INT1, en estos puertos siempre se genera una interrupción al haber un cambio en la entrada, ya sea soltar y pulsar.
PCICR |= (1 << PCIE1);
Sería PCIE0 para el registro B, y PCIE2 para el registro D
PCMSK1 |= (1 << PCINT11);
El registro a modificar depende del registro de puerto que estemos usando, PCMSK0 para el registro B, PCMSK1 para el registro C, PCMSK2 para el registro D
ISR(PCINT1_vect) {}
El argumento depende del registro del puerto, sería PCINT0_vect para el registro B, y PCINT2_vect para el registro D.
Dentro del código de la función ISR deberíamos de comprobar cual es el puerto que genera la interrupción, ya que podemos tener varios puertos activos en un solo registro de puertos.
sei();
Cuando se genera una interrupción y se ejecuta la funcion ISR(), en ese momento si se produce una nueva interrupción no se interrumpe la propia llamada a ISR(), si no que una vez acaba la ejecución de la ISR() actual se lanza esta nueva excepción que se ha producido durante la ejecución de la excepción.
Solo se almacena una interrupción que se haya producido, si se producen 5 interrupciones NO se ejecutará ISR() 5 veces.
El problema que tenemos a veces es que si tenemos un botón conectado a un puerto para que genere una interrupción este puede generar un pequeño rebote al ser pulsado y hacer que una interrupción se ejecute varias veces, para evitar esto podemos incluir un pequeño delay en la función ISR() y al final de la función incluir el siguiente código para limpiar posibles interrupciónes que se hayan producido durante la ejecución de la propia ISR():
Para INT0
EIFR |= (1 << INTF0);
Para INT1
EIFR |= (1 << INTF1);
Para Registro B
PCIFR |= (1 << PCIF0);
Para Registro C
PCIFR |= (1 << PCIF1);
Para Registro D
PCIFR |= (1 << PCIF2);
AVR | microcontrolador | hardware interrupts